﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using Noris.Schedule.Support.Sql;
using System.Xml.Linq;
using Noris.Schedule.Support.Parser;
using System.Windows.Forms;
using Noris.Schedule.Support;
using System.Drawing;

namespace Noris.Tools.FrxEditor.Data
{
    #region class ReportData : Třída, která tvoří datový základ jedné části reportu (reportu, grupy, grafu...)
    /// <summary>
    /// ReportData : Třída, která tvoří datový základ jedné části reportu (reportu, grupy, grafu...)
    /// </summary>
    public class ReportData
    {
        #region Konstrukce
        public ReportData()
        {
            this.Columns = new ReportDataColumnCollection();
            this.Parameters = new ReportDataParamCollection();
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if (this.Columns.Count > 0)
                return this.Columns.Count.ToString() + " column" + (this.Columns.Count > 1 ? "s" : "");
            if (!String.IsNullOrEmpty(this.DataQuery))
            {
                string query = this.DataQuery;
                int queryLen = query.Length;
                if (queryLen > 16)
                    query = query.Substring(0, 16) + "...";
                query = query
                    .Replace('\r', ' ')
                    .Replace('\n', ' ')
                    .Replace('\t', ' ');
                return query;
            }
            return "Empty";
        }
        #endregion
        #region Property s daty
        /// <summary>
        /// Název datového zdroje
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Data nejsou definována (ani SQL dotaz, ani sloupce)
        /// </summary>
        public bool IsEmpty
        {
            get { return String.IsNullOrEmpty(this.DataQuery) && this.Columns.Count == 0; }
        }
        /// <summary>
        /// Číslo výchozí třídy pro tvorbu SQL dotazu
        /// </summary>
        public int RootClassNumber { get; set; }
        /// <summary>
        /// SQL dotaz pro získání dat
        /// </summary>
        public string DataQuery { get; set; }
        /// <summary>
        /// Umožnit ruční editaci dat
        /// </summary>
        public bool DataQueryEditEnabled { get; set; }
        /// <summary>
        /// Deklarace sloupců datové tabulky (jak jsou deklarovány v reportu, včetně jejich vlastností)
        /// </summary>
        public ReportDataColumnCollection Columns { get; set; }
        /// <summary>
        /// Deklarace argumentů pro SQL dotaz
        /// </summary>
        public ReportDataParamCollection Parameters { get; set; }
        /// <summary>
        /// Tabulka s načtenými daty
        /// </summary>
        public SimpleDataTable Table { get; private set; }
        /// <summary>
        /// Vyprázdní svoje datové struktury
        /// </summary>
        public void Clear()
        {
            this.RootClassNumber = 0;
            this.DataQuery = null;
            this.Columns = new ReportDataColumnCollection();
            this.Parameters = new ReportDataParamCollection();
            this.Table = null;
        }
        /// <summary>
        /// Načte do své tabulky (Table) data podle aktuálního dotazu (DataQuery)
        /// </summary>
        /// <param name="paramValue">Druh parametrů: pro běh a náhled / pro editaci</param>
        public SimpleDataTable LoadData(ReportParamValue paramValue)
        {
            string sqlCommand = this.Parameters.ApplyParamToCommand(this.DataQuery, paramValue);
            SimpleDataTable table = SimpleDataTable.Load(sqlCommand);
            return table;
        }
        #endregion
        #region Serializace dat
        /// <summary>
        /// Zajistí uložení datové části Bandu do XML elementu
        /// </summary>
        /// <param name="node"></param>
        public void XmlSerializeDataTo(XElement node)
        {
            XElement xData = new XElement(XML_REPORTDATA);
            xData.SetAttributeValue("Query", this.DataQuery);
            node.Add(xData);
        }
        /// <summary>
        /// Zajistí načtení datové části Bandu z XML elementu
        /// </summary>
        /// <param name="node"></param>
        /// <param name="overrideId"></param>
        public void XmlFillDataFrom(XElement node, bool overrideId)
        {
        }
        /// <summary>
        /// Název XML elementu, v němž je obsažena definice dat bandu (ReportData)
        /// </summary>
        public const string XML_REPORTDATA = "datasource";
        #endregion
        #region Vizuální editace dat
        /// <summary>
        /// Zobrazí formulář umožňující zadání dat
        /// </summary>
        public void ShowSettings()
        {
            using (ReportData_Form form = new ReportData_Form())
            {
                form.ReportData = this; // .EditedText = "SELECT top 100 * FROM lcs.subjekty"; // "SELECT * FROM lcs.subjekty s LEFT OUTER JOIN lcs.tabulka t on t.cislo_subjektu = s.cislo_subjektu";
                form.ShowDialog(Steward.MainForm);
            }
        }
        #endregion
        #region ColumnDataType - konverze, konstanty
        /// <summary>
        /// Vrací typ parametru dle názvu typu
        /// </summary>
        /// <param name="typeName"></param>
        /// <returns></returns>
        public static ReportColumnDataType ConvertParamType(string typeName)
        {
            if (String.IsNullOrEmpty(typeName)) return ReportColumnDataType.None;
            switch (typeName)
            {
                case PARAMTYPE_INT: return ReportColumnDataType.Int;
                case PARAMTYPE_DECIMAL: return ReportColumnDataType.Decimal;
                case PARAMTYPE_CHARACTER: return ReportColumnDataType.Character;
                case PARAMTYPE_DATE: return ReportColumnDataType.Date;
            }

            // Konverze z názvů typů z powerbuilderu a z Noris.Atribute.Typ:
            if (typeName.StartsWith("long")) return ReportColumnDataType.Int;
            if (typeName.StartsWith("char")) return ReportColumnDataType.Character;
            if (typeName.StartsWith("nchar")) return ReportColumnDataType.Character;
            if (typeName.StartsWith("varchar")) return ReportColumnDataType.Character;
            if (typeName.StartsWith("nvarchar")) return ReportColumnDataType.Character;
            if (typeName.StartsWith("text")) return ReportColumnDataType.Character;
            if (typeName.StartsWith("ntext")) return ReportColumnDataType.Character;
            if (typeName.StartsWith("decimal")) return ReportColumnDataType.Decimal;
            if (typeName.StartsWith("datetime")) return ReportColumnDataType.Date;
            if (typeName.StartsWith("number")) return ReportColumnDataType.Decimal;

            return ReportColumnDataType.None;
        }
        /// <summary>
        /// Vrací název typu parametru dle jeho typu
        /// </summary>
        /// <param name="sourceType"></param>
        /// <returns></returns>
        public static string ConvertParamType(ReportColumnDataType paramType)
        {
            switch (paramType)
            {
                case ReportColumnDataType.None: return null;
                case ReportColumnDataType.Int: return PARAMTYPE_INT;
                case ReportColumnDataType.Decimal: return PARAMTYPE_DECIMAL;
                case ReportColumnDataType.Character: return PARAMTYPE_CHARACTER;
                case ReportColumnDataType.Date: return PARAMTYPE_DATE;
            }
            return null;
        }
        public const string PARAMTYPE_INT = "int";
        public const string PARAMTYPE_DECIMAL = "decimal";
        public const string PARAMTYPE_CHARACTER = "char";
        public const string PARAMTYPE_DATE = "date";
        /// <summary>
        /// Názvy typů parametrů. Lze použít jako podklad editačního stylu (EditStyleValues).
        /// </summary>
        public const string PARAM_TYPES = PARAMTYPE_INT + "," + PARAMTYPE_DECIMAL + "," + PARAMTYPE_CHARACTER + "," + PARAMTYPE_DATE;
        #endregion
        #region ReportParamSourceType - konverze, konstanty
        /// <summary>
        /// Vrací typ zdroje dle názvu zdroje
        /// </summary>
        /// <param name="sourceName"></param>
        /// <returns></returns>
        public static ReportParamSourceType ConvertSourceType(string sourceName)
        {
            if (String.IsNullOrEmpty(sourceName)) return ReportParamSourceType.None;
            switch (sourceName)
            {
                case SOURCETYPE_INPUT: return ReportParamSourceType.Input;
                case SOURCETYPE_DIALOG: return ReportParamSourceType.Dialog;
            }

            return ReportParamSourceType.None;
        }
        /// <summary>
        /// Vrací název typu parametru dle jeho typu
        /// </summary>
        /// <param name="sourceType"></param>
        /// <returns></returns>
        public static string ConvertSourceType(ReportParamSourceType sourceType)
        {
            switch (sourceType)
            {
                case ReportParamSourceType.None: return null;
                case ReportParamSourceType.Input: return SOURCETYPE_INPUT;
                case ReportParamSourceType.Dialog: return SOURCETYPE_DIALOG;
            }
            return null;
        }
        public const string SOURCETYPE_INPUT = "Selected records";
        public const string SOURCETYPE_DIALOG = "Dialog form";
        /// <summary>
        /// Názvy typů zdroje parametru. Lze použít jako podklad editačního stylu (EditStyleValues).
        /// </summary>
        public const string SOURCE_TYPES = SOURCETYPE_INPUT + "," + SOURCETYPE_DIALOG;
        #endregion
        #region Práce s parametry v SQL příkazu
        /// <summary>
        /// V dodaném textu vyhledá a vrátí všechny názvy formálních parametrů (mají tvar  :name )
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        internal static List<string> FindParameterNames(string command)
        {
            List<ParserSegmentValue> values = FindParameterValues(command);
            return values.ConvertAll<string>(v => v.Text);
        }
        /// <summary>
        /// V dodaném textu vyhledá a vrátí všechny prvky (ParserSegmentValue) formálních parametrů (mají tvar  :name )
        /// </summary>
        /// <param name="segments"></param>
        /// <returns></returns>
        internal static List<ParserSegmentValue> FindParameterValues(string command)
        {
            IEnumerable<ParserSegment> segments = Parser.ParseString(command, ParserDefaultSetting.MsSql);
            return FindParameterValues(segments);
        }
        /// <summary>
        /// V dodaných segmentech vyhledá a vrátí všechny prvky (ParserSegmentValue) formálních parametrů (mají tvar  :name )
        /// </summary>
        /// <param name="segments"></param>
        /// <returns></returns>
        internal static List<ParserSegmentValue> FindParameterValues(IEnumerable<ParserSegment> segments)
        {
            List<ParserSegmentValue> result = new List<ParserSegmentValue>();
            if (segments != null)
            {
                foreach (ParserSegment segment in segments)
                    FindParameterValues(segment, result);
            }
            return result;
        }
        /// <summary>
        /// Z dodaného segmentu vyhledá a do resultu vloží všechny prvky (ParserSegmentValue) formálních parametrů (mají tvar  :name )
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="result"></param>
        private static void FindParameterValues(ParserSegment segment, List<ParserSegmentValue> result)
        {
            foreach (ParserSegmentValue value in segment.ValueList)
            {
                switch (value.ValueType)
                {
                    case ParserSegmentValueType.Text:
                        if (FindParameterIsParam(value.Text))
                            result.Add(value);
                        break;
                    case ParserSegmentValueType.InnerSegment:
                        FindParameterValues(value.InnerSegment, result);
                        break;
                }
            }
        }
        /// <summary>
        /// Vrátí true, pokud daný text vypadá jako formálně zapsaný parametr (má tvar  :name )
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private static bool FindParameterIsParam(string text)
        {
            if (String.IsNullOrEmpty(text)) return false;
            string t = text.Trim();
            return (t.Length > 1 && t[0] == ':');
        }
        #endregion
    }
    /// <summary>
    /// Datový typ parametru
    /// </summary>
    public enum ReportColumnDataType
    {
        None = 0,
        Int,
        Decimal,
        Character,
        Date
    }
    /// <summary>
    /// Zdroj vstupu hodnoty parametru
    /// </summary>
    public enum ReportParamSourceType
    {
        /// <summary>
        /// Neurčeno
        /// </summary>
        None = 0,
        /// <summary>
        /// Vstupní hodnota (čísla [non]subjektů, pro které se sestava tiskne / pro které se select načítá)
        /// </summary>
        Input,
        /// <summary>
        /// Dialogová hodnota (dialog před tiskem sestavy)
        /// </summary>
        Dialog
    }
    #endregion
    #region class ReportDataColumnCollection : kolekce sloupců včetně funkcionality
    /// <summary>
    /// ReportDataColumnCollection : kolekce sloupců včetně funkcionality
    /// </summary>
    public class ReportDataColumnCollection : Collection<ReportDataColumn>
    { 
        #region Konstrukce
        /// <summary>
        /// Konstruktor
        /// </summary>
        public ReportDataColumnCollection()
            : base(StringComparison.OrdinalIgnoreCase)
        {
        }
        /// <summary>
        /// vrátí klíčový název z dané položky
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        protected override string GetName(ReportDataColumn item)
        {
            return item.ColumnAlias;
        }
        #endregion
        #region Přidávání položek
        /// <summary>
        /// Přidá column, který není zadán ze stromu tříd.
        /// Používá se při importu z PowerBuilderu.
        /// </summary>
        /// <param name="dbname">Název, pod nímž se data načtou z SQL serveru dotazem v Retrieve</param>
        /// <param name="name">Název používaný v reportu (v jednotlivých fields)</param>
        /// <param name="type">Název db typu, zde může být i název powerbuildrový</param>
        public void Add(string dbname, string name, string type)
        {
            ReportDataColumn dataColumn = new ReportDataColumn()
            {
                ClassFullPath = "",
                AttributeFullPath = "",
                ClassRelationName = "",
                AttributeName = name,
                AttributeTypeName = type,
                EditStyleName = "",
                ColumnAlias = dbname,
                ClassRelationToolTip = "",
                AttributeToolTip = "",
                ReportVariableName = name
            };
            this.Add(dataColumn);
        }
        #endregion
        #region Datová tabulka pro vizuální editaci
        /// <summary>
        /// Datová tabulka umožňující editaci dat.
        /// Get vytvoří novou tabulku ze svých dat.
        /// Set převezme obsah dodané tabulky do svých dat.
        /// </summary>
        public SimpleDataTable EditDataTable
        {
            get { return this._EditDataTableCreate(); }
            set { this._EditDataTableAccept(value); }
        }
        /// <summary>
        /// Vrátí nově vytvořenou tabulku s daty Columns
        /// </summary>
        /// <returns></returns>
        private SimpleDataTable _EditDataTableCreate()
        {
            SimpleDataTable table = CreateEditDataTable();
            foreach (ReportDataColumn dataColumn in this)
            {
                SimpleDataRow row = table.Rows.Add
                ( // Zadáváme přesně v pořadí sloupců v tabulce:
                    dataColumn.ClassFullPath, 
                    dataColumn.AttributeFullPath, 
                    dataColumn.ClassRelationName,
                    dataColumn.AttributeName, 
                    dataColumn.EditStyleName, 
                    dataColumn.ColumnAlias,
                    dataColumn.AttributeTypeName,
                    dataColumn.ReportVariableName
                );
                row.Cells["class_name"].ToolTipText = dataColumn.ClassRelationToolTip;
                row.Cells["attr_name"].ToolTipText = dataColumn.AttributeToolTip;
            }
            return table;
        }
        /// <summary>
        /// Převezme řádky z dodané tabulky a uloží je do sebe jako Columns
        /// </summary>
        /// <param name="table"></param>
        private void _EditDataTableAccept(SimpleDataTable table)
        {
            this.Clear();
            foreach (SimpleDataRow row in table.Rows)
            {
                ReportDataColumn dataColumn = new ReportDataColumn()
                {
                    ClassFullPath = row.Get<string>("class_id"),
                    AttributeFullPath = row.Get<string>("column_id"),
                    ClassRelationName = row.Get<string>("class_name"),
                    AttributeName = row.Get<string>("attr_name"),
                    EditStyleName = row.Get<string>("edit_style"),
                    ColumnAlias = row.Get<string>("column_alias"),
                    AttributeTypeName = row.Get<string>("data_type"),
                    ReportVariableName = row.Get<string>("report_name"),
                    ClassRelationToolTip = row.Cells["class_name"].ToolTipText,
                    AttributeToolTip = row.Cells["attr_name"].ToolTipText
                };
                this.Add(dataColumn);
            }
        }
        /// <summary>
        /// Určí vhodný alias pro sloupec daného jména.
        /// Pokud v tabulce dosud není takový název použit, vrátí se nezměněn (přesněji řečeno, vrací se lower, trim).
        /// Pokud sloupec takového jména již existuje, hledá postupně názvy se suffixem "_1", "_2", ... "_999" a vrátí první volný alias.
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="table"></param>
        /// <returns></returns>
        public static string CreateColumnAlias(string columnName, SimpleDataTable table)
        {
            Dictionary<string, object> aliasDict = new Dictionary<string, object>();
            foreach (SimpleDataRow row in table.Rows)
            {
                string columnAlias = row.Get<string>(5).ToLower().Trim();
                if (!aliasDict.ContainsKey(columnAlias))
                    aliasDict.Add(columnAlias, null);
            }

            string aliasBase = columnName.ToLower().Trim();
            for (int s = 0; s < 1000; s++)
            {
                string alias = aliasBase + (s == 0 ? "" : "_" + s.ToString());
                if (!aliasDict.ContainsKey(alias))
                    return alias;
            }

            return null;
        }
        /// <summary>
        /// Metoda vytvoří a vrátí prázdnou tabulku se strukturou odpovídající tabulce ReportDataColumnCollection.EditDataTable
        /// </summary>
        /// <returns></returns>
        public static SimpleDataTable CreateEditDataTable()
        {
            SimpleDataTable table = SimpleDataTable.Empty;
            SimpleDataColumn column;
            column = table.Columns.Add("class_id", typeof(string));
            column.GridVisible = false;

            column = table.Columns.Add("column_id", typeof(string));
            column.GridVisible = false;

            column = table.Columns.Add("class_name", "Vztah - Třída", typeof(string));
            column.ColumnWidth = 170;
            column.ShowOnlyDifferentValuesByColumn = table.Columns[0];

            column = table.Columns.Add("attr_name", "Atribut", typeof(string));
            column.ColumnWidth = 160;
            column.ToolTipText = "Název atributu";

            column = table.Columns.Add("edit_style", "Editační styl", typeof(string));
            column.ColumnWidth = 135;
            column.EditStyleValues = EditStyleList.EditStylesValues;

            column = table.Columns.Add("column_alias", "Alias sloupce", typeof(string));
            column.ColumnWidth = 125;

            column = table.Columns.Add("data_type", "Datový typ", typeof(string));
            column.ColumnWidth = 100;
            column.EditStyleValues = ReportData.PARAM_TYPES;

            column = table.Columns.Add("report_name", "Název proměnné v reportu", typeof(string));
            column.GridVisible = false;

            return table;
        }
        #endregion
    }
    /// <summary>
    /// Data popisující jeden sloupec s daty
    /// </summary>
    public class ReportDataColumn
    {
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return (String.IsNullOrEmpty(this.AttributeName) ? "" : this.AttributeName + ": ") + this.ColumnAlias;
        }
        /// <summary>
        /// Alias sloupce v selectu. Musí být jednoznačné (OrdinalIgnoreCase).
        /// Po načtení dat selectem bude mít db sloupec tento název.
        /// </summary>
        public string ColumnAlias { get; set; }
        /// <summary>
        /// Plná cesta ke třídě, která se načítá. 
        /// Má formu "C118:R9260:C18:L122", kde 
        /// C uvozuje číslo třídy, 
        /// R vztah doprava (typicky statický z našeho záznamu do číselníku),
        /// L vztah doleva (typicky všechny daové záznamy, odkazující se na náš číselník).
        /// </summary>
        public string ClassFullPath { get; set; }
        /// <summary>
        /// Plná cesta k atributu.
        /// Začíná ClassFullPath, následuje separátor (&gt;), poté druh tabulky ve třídě, tečka a column atributu z tabulky.
        /// </summary>
        public string AttributeFullPath { get; set; }
        /// <summary>
        /// Název typu dat (string).
        /// Probíhá online konverze na enum z/do AttributeType.
        /// </summary>
        public string AttributeTypeName
        {
            get { return ReportData.ConvertParamType(this.AttributeType); }
            set { this.AttributeType = ReportData.ConvertParamType(value); }
        }
        /// <summary>
        /// Datový typ hodnoty atributu
        /// </summary>
        public ReportColumnDataType AttributeType { get; set; }
        /// <summary>
        /// Název vztahu a/nebo třídy
        /// </summary>
        public string ClassRelationName { get; set; }
        /// <summary>
        /// Tooltip k vztahu a/nebo třídě
        /// </summary>
        public string ClassRelationToolTip { get; set; }
        /// <summary>
        /// Název atributu
        /// </summary>
        public string AttributeName { get; set; }
        /// <summary>
        /// Tooltip k atributu
        /// </summary>
        public string AttributeToolTip { get; set; }
        /// <summary>
        /// Název editačního stylu
        /// </summary>
        public string EditStyleName { get; set; }
        /// <summary>
        /// Název údaje, používaný v reportu.
        /// Pokud není zadán, obsahuje hodnotu z ColumnAlias.
        /// </summary>
        public string ReportVariableName
        {
            get { return (String.IsNullOrEmpty(this._ReportVariableName) ? this.ColumnAlias : this._ReportVariableName); }
            set { this._ReportVariableName = (String.Equals(value, this.ColumnAlias, StringComparison.OrdinalIgnoreCase) ? null : value); }
        }
        private string _ReportVariableName;
    }
    /// <summary>
    /// Pomocná statická třída, která načítá z databáze seznam (názvy) všech editačních stylů.
    /// Seznam je obsažen ve statické property EditStylesValues a lze jej přímo použít do SimpleDataColumn.EditStyleValues
    /// </summary>
    public class EditStyleList
    {
        /// <summary>
        /// Vrátí soupis názvů všech editačních stylů v systému, včetně na prvním místě prázdného prvku.
        /// Tento seznam lze vložit do SimpleDataColumn.EditStyleValues, pak může uživatel vybrat jakýkoli existující editační styl.
        /// Seznam má v první pozici prázdnou položku (=bez editačního stylu).
        /// </summary>
        public static string EditStylesValues
        {
            get
            {
                if (_EditStylesValues == null)
                    _EditStylesValues = _LoadEditStyles();
                return _EditStylesValues;
            }
        }
        private static string _EditStylesValues = null;
        private static string _LoadEditStyles()
        {
            string sql = "SELECT distinct name FROM lcs.editstyles ORDER BY name";
            List<string> esList = Db_Layer.GetListString(sql);
            StringBuilder sb = new StringBuilder();
            sb.Append("");
            foreach (string es in esList)
                sb.Append(";" + es);
            return sb.ToString();
        }
    }
    #endregion
    #region class ReportDataParamCollection : kolekce parametrů včetně funkcionality.
    /// <summary>
    /// ReportDataParamCollection : kolekce parametrů včetně funkcionality.
    /// Slouží i k modifikaci SQL selectu.
    /// </summary>
    public class ReportDataParamCollection : Collection<ReportDataParam>
    {
        #region Konstrukce
        /// <summary>
        /// Konstruktor
        /// </summary>
        public ReportDataParamCollection()
            : base(StringComparison.OrdinalIgnoreCase)
        {
        }
        /// <summary>
        /// vrátí klíčový název z dané položky
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        protected override string GetName(ReportDataParam item)
        {
            return item.ParamName;
        }
        #endregion
        #region Přidávání položek
        /// <summary>
        /// Přidá další parametr
        /// </summary>
        /// <param name="paramName"></param>
        /// <param name="paramTypeName"></param>
        public void Add(string paramName, string paramTypeName)
        {
            this.Add(new ReportDataParam() { ParamName = paramName, ParamTypeName = paramTypeName });
        }
        /// <summary>
        /// Přidá další parametr
        /// </summary>
        /// <param name="paramName"></param>
        /// <param name="paramType"></param>
        public void Add(string paramName, ReportColumnDataType paramType)
        {
            this.Add(new ReportDataParam() { ParamName = paramName, ParamType = paramType });
        }
        #endregion
        #region Datová tabulka pro vizuální editaci
        /// <summary>
        /// Datová tabulka umožňující editaci dat.
        /// Get vytvoří novou tabulku ze svých dat.
        /// Set převezme obsah dodané tabulky do svých dat.
        /// </summary>
        public SimpleDataTable EditDataTable
        {
            get { return this._EditDataTableCreate(); }
            set { this._EditDataTableAccept(value); }
        }
        /// <summary>
        /// Vrátí nově vytvořenou tabulku s daty Columns
        /// </summary>
        /// <returns></returns>
        private SimpleDataTable _EditDataTableCreate()
        {
            SimpleDataTable table = CreateEditDataTable();
            foreach (ReportDataParam dataParam in this)
            {
                SimpleDataRow row = table.Rows.Add
                (   // Zadáváme přesně v pořadí sloupců v tabulce:
                    dataParam.ParamName,
                    dataParam.ParamTypeName,
                    dataParam.SourceTypeName,
                    dataParam.EditValue,
                    "D",
                    dataParam.ParamName
                );
                row.CellStyle = new SimpleDataStyle();
                row.CellStyle[SimpleDataStyleState.Standard] = new SimpleDataStyleOne(Color.LimeGreen, SimpleDataStyleEffect.Outset, Color.BlanchedAlmond);
            }
            return table;
        }
        /// <summary>
        /// Převezme řádky z dodané tabulky a uloží je do sebe jako Columns
        /// </summary>
        /// <param name="table"></param>
        private void _EditDataTableAccept(SimpleDataTable table)
        {
            this.Clear();
            foreach (SimpleDataRow row in table.Rows)
            {
                ReportDataParam dataParam = new ReportDataParam()
                {
                    ParamName = row.Get<string>("param_name"),
                    ParamTypeName = row.Get<string>("param_type_name"),
                    SourceTypeName = row.Get<string>("param_source_name"),
                    EditValue = row.Get<string>("edit_value")
                };
                this.Add(dataParam);
            }
        }
        /// <summary>
        /// Metoda vytvoří a vrátí prázdnou tabulku se strukturou odpovídající tabulce ReportDataColumnCollection.EditDataTable
        /// </summary>
        /// <returns></returns>
        public static SimpleDataTable CreateEditDataTable()
        {
            SimpleDataTable table = SimpleDataTable.Empty;
            SimpleDataColumn column;
            column = table.Columns.Add("param_name", "Název parametru", typeof(string));
            column.ColumnWidth = 120;

            column = table.Columns.Add("param_type_name", "Datový typ", typeof(string));
            column.ColumnWidth = 120;
            column.EditStyleValues = ReportData.PARAM_TYPES;

            column = table.Columns.Add("param_source_name", "Zdroj dat", typeof(string));
            column.ColumnWidth = 120;
            column.EditStyleValues = ReportData.SOURCE_TYPES;

            column = table.Columns.Add("edit_value", "Hodnota v době editace", typeof(string));
            column.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;

            column = table.Columns.Add("param_origin_source", "Původ parametru", typeof(string));           // "D" = z ParamData / "F" = z formuláře, neserializuje se
            column.GridVisible = false;

            column = table.Columns.Add("param_origin_name", "Původní název parametru", typeof(string));     // Původní jméno parametru, neserializuje se
            column.GridVisible = false;

            return table;
        }
        #endregion


        #region Aplikace parametrů do SQL příkazu
        /// <summary>
        /// Zajistí, že v soupisu parametrů bude jeden parametr typu Int označen jako Input = deklarující čísla záznamů, pro něž se report spouští
        /// </summary>
        public void CheckFirstNumericAsInput()
        {
            if (this._Items.Count == 0) return;
            if (this._Items.Any(p => p.ParamType == ReportColumnDataType.Int && p.SourceType == ReportParamSourceType.Input)) return;  // OK, už tam nějaký je
            ReportDataParam param = this._Items.FirstOrDefault(p => p.ParamType == ReportColumnDataType.Int);
            if (param == null) return;
            param.SourceType = ReportParamSourceType.Input;
        }
        /// <summary>
        /// Aplikuje do dodaného SQL příkazu své parametry
        /// </summary>
        /// <param name="command"></param>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        internal string ApplyParamToCommand(string command, ReportParamValue paramValue)
        {
            if (this.IsEmpty) return command;

            StringBuilder sb = new StringBuilder();
            List<ParserSegment> segments = Parser.ParseString(command, ParserDefaultSetting.MsSql);
            foreach (ParserSegment segment in segments)
            {
                this.ApplyParamToSegment(segment, paramValue);
                sb.AppendLine(segment.Text);
            }
            return sb.ToString();
        }

        private void ApplyParamToSegment(ParserSegment segment, ReportParamValue paramValue)
        {
            foreach (ParserSegmentValue value in segment.Values)
            {
                if (value.ValueType == ParserSegmentValueType.Text && value.Content.Trim().StartsWith(":"))
                    this.ApplyParamToValue(value, paramValue);
                else if (value.HasInnerSegment)
                    ApplyParamToSegment(value.InnerSegment, paramValue);
            }
        }
        private void ApplyParamToValue(ParserSegmentValue value, ReportParamValue paramValue)
        {
            string paramName = value.Content.Trim();            // Název parametru v textu začíná :

            object data;
            if (this.TryGetValue(paramName, paramValue, out data))
            {
                string text = ((string)data).Trim();

                // text může být jeden údaj (typicky číslo subjektu), nebo výraz, pak ale filtr musíme upravit.
                // Výchozí tvar filtru je: "SELECT WHERE column = :param" anebo "EXEC ... @paramname = :param".
                // Pokud jde o SELECT a fráze je ve WHERE, a text obsahuje výraz, pak filtr změníme na: "...WHERE column IN (text)".
                bool isExpression = (text.Contains(' ') || text.Contains(','));
                if (isExpression)
                {
                    this.ApplyParamToValueChangeExpr(value);
                    value.SetContent("(" + text + ")");
                }
                else
                {
                    value.SetContent(Noris.Schedule.Support.Sql.SqlFormat.ToSql(text));
                }
            }
        }
        /// <summary>
        /// Najde nejbližší text v segmentu před danou hodnotou (value), a pokud to bude rovnítko pak jej nahradí slovem IN.
        /// </summary>
        /// <param name="value"></param>
        private void ApplyParamToValueChangeExpr(ParserSegmentValue value)
        {
            ParserSegment segment = value.Parent;
            int index = value.CurrentIndex;
            while (index > 0)
            {
                index--;
                ParserSegmentValue valuePre = segment.ValueList[index];
                if (valuePre.ValueType == ParserSegmentValueType.Blank) continue;
                if (valuePre.ValueType == ParserSegmentValueType.Delimiter && valuePre.Content == "=")
                {
                    valuePre.SetContent(" in ", true);
                }
                break;
            }
        }
        private bool TryGetValue(string paramName, ReportParamValue paramValue, out object data)
        {
            data = null;
            ReportDataParam param = this._Items.FirstOrDefault(p => p.ParamName == paramName);
            if (param == null)
                return false;
            data = param.GetValue(paramValue);
            return true;
        }
        #endregion
        #region DataTable pro editaci parametrů
        /// <summary>
        /// Tabulka pro zobrazení parametrů
        /// </summary>
        public SimpleDataTable DataTable
        {
            get { return this._CreateDataTable(); }
        }
        private SimpleDataTable _CreateDataTable()
        {
            SimpleDataTable table = SimpleDataTable.Empty;
            table.Columns.Add("param_name", "Název", typeof(string));
            table.Columns.Add("param_type", "Typ", typeof(string));
            table.Columns[1].EditStyleValues = ReportData.PARAM_TYPES;
            table.Columns.Add("param_value", "Hodnota", typeof(string));
            foreach (ReportDataParam param in this._Items)
                table.Rows.Add(param.ParamName, param.ParamTypeName, param.EditValue);
            return table;
        }
        #endregion
    }
    /// <summary>
    /// ReportDataParam : popisuje jeden parametr SQL příkazu - název, typ, testovací hodnotu pro editaci.
    /// </summary>
    public class ReportDataParam
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        public ReportDataParam() { }
        /// <summary>
        /// Jméno parametru
        /// </summary>
        public string ParamName { get; set; }
        /// <summary>
        /// Název typu parametru (string)
        /// </summary>
        public string ParamTypeName
        {
            get { return ReportData.ConvertParamType(this.ParamType); }
            set { this.ParamType = ReportData.ConvertParamType(value); }
        }
        /// <summary>
        /// Typ parametru (enum)
        /// </summary>
        public ReportColumnDataType ParamType { get; set; }
        /// <summary>
        /// Název zdroje dat pro tento parametr (string)
        /// </summary>
        public string SourceTypeName
        {
            get { return ReportData.ConvertSourceType(this.SourceType); }
            set { this.SourceType = ReportData.ConvertSourceType(value); }
        }
        /// <summary>
        /// Zdroj dat pro tento parametr
        /// </summary>
        public ReportParamSourceType SourceType { get; set; }
        /// <summary>
        /// Hodnota při editaci
        /// </summary>
        public string EditValue { get; set; }
        /// <summary>
        /// Hodnota pro tisk reportu
        /// </summary>
        public string ReportValue { get; set; }
        /// <summary>
        /// Vrátí svůj klon
        /// </summary>
        /// <returns></returns>
        public ReportDataParam Clone()
        {
            return (ReportDataParam)this.MemberwiseClone();
        }
        /// <summary>
        /// Vrátí požadovanou hodnotu (pro editaci / pro report)
        /// </summary>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        internal object GetValue(ReportParamValue paramValue)
        {
            switch (paramValue)
            {
                case ReportParamValue.Edit: return this.EditValue;
                case ReportParamValue.Report: return this.ReportValue;
            }
            return null;
        }
    }
    /// <summary>
    /// Druh hodnoty parametru (editační / tisková)
    /// </summary>
    public enum ReportParamValue
    {
        /// <summary>
        /// Bez hodnoty
        /// </summary>
        None = 0,
        /// <summary>
        /// Hodnota pro editor
        /// </summary>
        Edit,
        /// <summary>
        /// Hodnota pro tisk i náhled reálného reportu
        /// </summary>
        Report
    }
    #endregion
}
